package com.example.understandvideo.tool;

import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.*;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;

/**
 * 封装了XML转换成object，object转换成XML的代码
 */
@Slf4j
public class XMLUtil {
    /**
     * 将对象直接转换成String类型的 XML输出
     *
     * @param obj
     * @return
     */
    public static String convertToXml(Object obj) {
        try {
            String result;
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();

            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                    Boolean.TRUE);

            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(baos, (String) marshaller.getProperty(Marshaller.JAXB_ENCODING));
                marshaller.marshal(obj, xmlStreamWriter);
                xmlStreamWriter.writeEndDocument();
                xmlStreamWriter.close();
                result = new String(baos.toByteArray());
            }
            return result;
        } catch (Exception e) {
            log.error("{}", e);
        }
        return null;
    }

    /**
     * 将对象直接转换成String类型的 XML输出
     *
     * @param obj
     * @return
     */
    public static String convertToCleanXml(Object obj) {
        try {
            String result;
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
            Marshaller marshaller = context.createMarshaller();
            // 配置Marshaller以生成更简洁的XML（但仍然包含命名空间）
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");

            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(baos, (String) marshaller.getProperty(Marshaller.JAXB_ENCODING));
                marshaller.marshal(obj, xmlStreamWriter);
                xmlStreamWriter.writeEndDocument();
                xmlStreamWriter.close();
                // 将XML字符串解析为DOM树
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                factory.setNamespaceAware(true);
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.parse(new ByteArrayInputStream(baos.toByteArray()));

                // 移除xmlns:xsi声明（注意：这通常不是必要的，因为它只是声明了一个前缀）
                // 但为了完整性，这里还是包含了这个步骤
                Element root = doc.getDocumentElement();

                // 递归地移除xsi:type属性
                removeXsiTypeAttributes(root);

                // 将修改后的DOM树转换回XML字符串
                TransformerFactory transformerFactory = TransformerFactory.newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                transformer.setOutputProperty("omit-xml-declaration", "yes");
                StringWriter resultWriter = new StringWriter();
                transformer.transform(new DOMSource(doc), new StreamResult(resultWriter));
                result = resultWriter.toString();
            }
            return result;
        } catch (Exception e) {
            log.error("{}", e);
        }
        return null;
    }

    private static void removeXsiTypeAttributes(Element element) {
        NamedNodeMap attributes = element.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Node attr = attributes.item(i);
            if ("xsi:type".equals(attr.getNodeName())) {
                element.removeAttributeNode((Attr) attr);
            }
        }
        NodeList children = element.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (child instanceof Element) {
                removeXsiTypeAttributes((Element) child);
            }
        }
    }

    /**
     * 将对象根据路径转换成xml文件
     *
     * @param obj
     * @param path
     * @return
     */
    public static void convertToXml(Object obj, String path) {
        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                    Boolean.TRUE);
            try (FileWriter fw = new FileWriter(path)) {
                marshaller.marshal(obj, fw);
            }
        } catch (Exception e) {
            log.error("{}", e);
        }
    }

    @SuppressWarnings("unchecked")
    /**
     * 将String类型的xml转换成对象
     */
    public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            try (StringReader sr = new StringReader(xmlStr)) {
                xmlObject = unmarshaller.unmarshal(sr);
            }
        } catch (Exception e) {
            log.error("{}", e);
        }
        return xmlObject;
    }

    @SuppressWarnings("unchecked")
    /**
     * 将file类型的xml转换成对象
     */
    public static Object convertXmlFileToObject(Class clazz, String xmlPath) {
        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            try (FileReader fr = new FileReader(xmlPath)) {
                xmlObject = unmarshaller.unmarshal(fr);
            }
        } catch (Exception e) {
            log.error("{}", e);
        }
        return xmlObject;
    }

}